﻿//Project: ClipFlair (http://ClipFlair.codeplex.com)
//Filename: BaseView.cs
//Version: 20141025

using System;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Media;

namespace ClipFlair.Windows.Views
{

  [ScriptableType]
  [DataContract(Namespace = "http://clipflair.net/Contracts/View")]
  public class BaseView: IView
  {

    #region --- Initialization ---

    public BaseView()
    {
      SetDefaults(); //ancestors don't need to call "SetDefaults" at their constructors since this constructor is always called
      TitleBackgroundColor = BorderColor; //set to same value as BorderColor (for compatibility with older save data that didn't have TitleBackgroundColor info) //at SetDefaults using DefaultTitleBackgroundColor special value that is detected at OnDeserialized and replaced with BorderColor
    }

    public BaseView(IView view) //only used if some descendent view needs to have a constructor that accepts IView (there it calls this base constructor)
    {
      if (view == null) return;

      Busy = view.Busy;
      //
      Time = view.Time;
      OptionsSource = view.OptionsSource;
      ID = view.ID;
      Title = view.Title;
      Position = view.Position; //no need to set X and Y, they're X and Y field accessors for Position property
      Width = view.Width;
      Height = view.Height;
      Zoom = view.Zoom;
      ZIndex = view.ZIndex;
      Opacity = view.Opacity;
      CornerRadius = view.CornerRadius;
      BorderThickness = view.BorderThickness;
      BorderColor = view.BorderColor;
      TitleForegroundColor = view.TitleForegroundColor;
      TitleBackgroundColor = view.TitleBackgroundColor;
      BackgroundColor = view.BackgroundColor;
      Moveable = view.Moveable;
      Resizable = view.Resizable;
      Zoomable = view.Zoomable;
      WarnOnClosing = view.WarnOnClosing;
      RTL = view.RTL;
      
      //Dirty flag
      Dirty = view.Dirty; //must be last - any overriden versions should also set this
    }

    #endregion

    #region --- Fields ---

    private TimeSpan time;
    private bool dirty;
    private bool busy;
    private Uri optionsSource;
    private string id;
    private string title;
    private Point position;
    private double width;
    private double height;
    private double zoom;
    private int zIndex;
    private double opacity;
    private Color borderColor;
    private Color titleForegroundColor;
    private Color? titleBackgroundColor;
    private Color backgroundColor;
    private Thickness borderThickness;
    private CornerRadius cornerRadius;
    private bool moveable;
    private bool resizable;
    private bool zoomable;
    private bool warnOnClosing;
    private bool rtl;
    private bool titlebarVisible;

    #endregion

    #region --- Properties ---

    //not stored
    [DefaultValue(ViewDefaults.DefaultDirty)]
    public bool Dirty
    {
      get { return dirty; }
      set
      {
        if (value != dirty)
        {
          dirty = value;
          RaisePropertyChanged(IViewProperties.PropertyDirty);
        }
      }
    }

    //not stored
    [DefaultValue(ViewDefaults.DefaultBusy)]
    public bool Busy
    {
      get { return busy; }
      set
      {
        if (value != busy)
        {
          busy = value;
          RaisePropertyChanged(IViewProperties.PropertyBusy);
        }
      }
    }

    [DataMember]
    [DefaultValue(ViewDefaults.DefaultOptionsSource)]
    public Uri OptionsSource
    {
      get { return optionsSource; }
      set
      {
        if (value != optionsSource)
        {
          optionsSource = value;
          Dirty = true;
        }
      }
    }

    [DataMember]
    //unique IDs are autogenerated, no default ID exists
    public string ID
    {
      get { return id; }
      set
      {
        if (value != id)
        {
          id = value;
          RaisePropertyChanged(IViewProperties.PropertyID);
          Dirty = true;
        }
      }
    }    

    [DataMember]
    [DefaultValue(ViewDefaults.DefaultTitle)]
    public string Title
    {
      get { return title; }
      set
      {
        if (value != title)
        {
          title = value;
          RaisePropertyChanged(IViewProperties.PropertyTitle);
          Dirty = true;
        }
      }
    }    

    [DataMember]
    //[DefaultValue(ViewDefaults.DefaultPosition)] //doesn't support Point for DefaultValue
    public Point Position
    {
      get { return position; }
      set
      {
        if (value != position)
        {
          position = value;
          RaisePropertyChanged(IViewProperties.PropertyPosition);
          RaisePropertyChanged(IViewProperties.PropertyX);
          RaisePropertyChanged(IViewProperties.PropertyY);
          Dirty = true;
        }
      }
    }

    //not stored, just a wrapper for Position.X
    [DefaultValue(ViewDefaults.DefaultX)]
    public double X
    {
      get { return position.X; }
      set
      {
        if (value != position.X)
        {
          position.X = value;
          RaisePropertyChanged(IViewProperties.PropertyX);
          RaisePropertyChanged(IViewProperties.PropertyPosition);
          Dirty = true;
        }
      }
    }

    //not stored, just a wrapper for Position.Y
    [DefaultValue(ViewDefaults.DefaultY)]
    public double Y
    {
      get { return position.Y; }
      set
      {
        if (value != position.Y)
        {
          position.Y = value;
          RaisePropertyChanged(IViewProperties.PropertyY);
          RaisePropertyChanged(IViewProperties.PropertyPosition);
          Dirty = true;
        }
      }
    }

    [DataMember]
    [DefaultValue(ViewDefaults.DefaultWidth)]
    public double Width
    {
      get { return width; }
      set
      {
        if (value != width)
        {
          width = value;
          RaisePropertyChanged(IViewProperties.PropertyWidth);
          Dirty = true;
        }
      }
    }

    [DataMember]
    [DefaultValue(ViewDefaults.DefaultHeight)]
    public double Height
    {
      get { return height; }
      set
      {
        if (value != height)
        {
          height = value;
          RaisePropertyChanged(IViewProperties.PropertyHeight);
          Dirty = true;
        }
      }
    }

    [DataMember]
    [DefaultValue(ViewDefaults.DefaultZoom)]
    public double Zoom
    {
      get { return zoom; }
      set
      {
        if (value != zoom)
        {
          zoom = value;
          RaisePropertyChanged(IViewProperties.PropertyZoom);
          Dirty = true;
        }
      }
    }

    [DataMember]
    [DefaultValue(ViewDefaults.DefaultZIndex)]
    public int ZIndex
    {
      get { return zIndex; }
      set
      {
        if (value != zIndex)
        {
          zIndex = value;
          RaisePropertyChanged(IViewProperties.PropertyZIndex);
          Dirty = true;
        }
      }
    }

    [DataMember]
    [DefaultValue(ViewDefaults.DefaultOpacity)]
    public double Opacity
    {
      get { return opacity; }
      set
      {
        if (value != opacity)
        {
          opacity = value;
          RaisePropertyChanged(IViewProperties.PropertyOpacity);
          Dirty = true;
        }
      }
    }

    [DataMember]
    //[DefaultValue(ViewDefaults.DefaultCornerRadius)] //can't use static fields here (and we're forced to use one for CornerRadius, doesn't work with const)
    public CornerRadius CornerRadius
    {
      get { return cornerRadius; }
      set
      {
        if (value != cornerRadius)
        {
          cornerRadius = value;
          RaisePropertyChanged(IViewProperties.PropertyCornerRadius);
          Dirty = true;
        }
      }
    }

    [DataMember]
    //[DefaultValue(ViewDefaults.DefaultBorderThickness)] //can't use static fields here (and we're forced to use one for Thickness, doesn't work with const)
    public Thickness BorderThickness
    {
      get { return borderThickness; }
      set
      {
        if (value != borderThickness)
        {
          borderThickness = value;
          RaisePropertyChanged(IViewProperties.PropertyBorderThickness);
          Dirty = true;
        }
      }
    }

    [DataMember]
    //[DefaultValue(ViewDefaults.DefaultBorderColor)] //can't use static fields here (and we're forced to use one for Color, doesn't work with const)
    public Color BorderColor
    {
      get { return borderColor; }
      set
      {
        if (value != borderColor)
        {
          borderColor = value;
          RaisePropertyChanged(IViewProperties.PropertyBorderColor);
          Dirty = true;
        }
      }
    }

    [DataMember]
    //[DefaultValue(ViewDefaults.DefaultTitleForegroundColor)]
    public Color TitleForegroundColor
    {
      get { return titleForegroundColor; }
      set
      {
        if (value != titleForegroundColor)
        {
          titleForegroundColor = value;
          RaisePropertyChanged(IViewProperties.PropertyTitleForegroundColor);
          Dirty = true;
        }
      }
    }
    
    [DataMember]
    //[DefaultValue(ViewDefaults.DefaultTitleBackgroundColor)] //DefaultTitleBackgroundColor is null, so that is becomes same as border color for compatibility with older saved data that didn't have title color (see "OnDeserialized" method)
    public Color? TitleBackgroundColor
    {
      get { return titleBackgroundColor; }
      set
      {
        if (value != titleBackgroundColor)
        {
          titleBackgroundColor = value;
          RaisePropertyChanged(IViewProperties.PropertyTitleBackgroundColor);
          Dirty = true;
        }
      }
    }

    [DataMember]
    //[DefaultValue(ViewDefaults.DefaultBackgroundColor)] //can't use static fields here (and we're forced to use one for Color, doesn't work with const)
    public Color BackgroundColor
    {
      get { return backgroundColor; }
      set
      {
        if (value != backgroundColor)
        {
          backgroundColor = value;
          RaisePropertyChanged(IViewProperties.PropertyBackgroundColor);
          Dirty = true;
        }
      }
    }
    
    [DataMember]
    [DefaultValue(ViewDefaults.DefaultMoveable)]
    public bool Moveable
    {
      get { return moveable; }
      set
      {
        if (value != moveable)
        {
          moveable = value;
          RaisePropertyChanged(IViewProperties.PropertyMoveable);
          Dirty = true;
        }
      }
    }
    
    [DataMember]
    [DefaultValue(ViewDefaults.DefaultResizable)]
    public bool Resizable
    {
      get { return resizable; }
      set
      {
        if (value != resizable)
        {
          resizable = value;
          RaisePropertyChanged(IViewProperties.PropertyResizable);
          Dirty = true;
        }
      }
    }

    [DataMember]
    [DefaultValue(ViewDefaults.DefaultZoomable)]
    public bool Zoomable
    {
      get { return zoomable; }
      set
      {
        if (value != zoomable)
        {
          zoomable = value;
          RaisePropertyChanged(IViewProperties.PropertyZoomable);
          Dirty = true;
        }
      }
    }

    [DataMember]
    [DefaultValue(ViewDefaults.DefaultWarnOnClosing)]
    public bool WarnOnClosing
    {
      get { return warnOnClosing; }
      set
      {
        if (value != warnOnClosing)
        {
          warnOnClosing = value;
          RaisePropertyChanged(IViewProperties.PropertyWarnOnClosing);
          Dirty = true;
        }
      }
    }

    [DataMember(Order = 0)] //using maximum order so that this gets deserialized after other fields (that don't have order set)
    //[DefaultValue(ViewDefaults.DefaultTime)] //can't use static fields here (and we're forced to use one for TimeSpan unfortunately, doesn't work with const)
    public virtual TimeSpan Time
    {
      get { return time; }
      set
      {
        if (value != time)
        {
          time = value;
          RaisePropertyChanged(IViewProperties.PropertyTime);
          //Dirty = true; //not considering playing to be an editing action
        }
      }
    }

    [DataMember]
    [DefaultValue(ViewDefaults.DefaultRTL)]
    public bool RTL
    {
      get { return rtl; }
      set
      {
        if (value != rtl)
        {
          rtl = value;
          RaisePropertyChanged(IViewProperties.PropertyRTL);
          Dirty = true;
        }
      }
    }

    [DataMember]
    [DefaultValue(ViewDefaults.DefaultTitlebarVisible)]
    public bool TitlebarVisible
    {
      get { return titlebarVisible; }
      set
      {
        if (value != titlebarVisible)
        {
          titlebarVisible = value;
          RaisePropertyChanged(IViewProperties.PropertyTitlebarVisible);
          Dirty = true;
        }
      }
    }

    #endregion

    #region --- Methods ---

    public virtual void SetDefaults()
    {
      ViewDefaults.SetDefaults(this); //this makes sure we set public properties (invoking "set" accessors), not fields
    }

    [OnDeserializing()] //this is called before deserialization occurs to set defaults for any properties that may be missing at the serialized data (e.g. from older serialized state)
    public void OnDeserializing(StreamingContext context) //Note that this cannot be a virtual method
    {
      SetDefaults(); //this one is a virtual method overriden by descendents (they should call "base.SetDefaults()" first there, then override any BaseView default values and then set defaults for their own properties)
    }

    [OnDeserialized()]
    public void OnDeserialized(StreamingContext context)
    {
      if (titleBackgroundColor == ViewDefaults.DefaultTitleBackgroundColor)
        titleBackgroundColor = borderColor;
    }

    #endregion

    #region --- Events ---

    //INotifyPropertyChanged//

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string PropertyName)
    {
      if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
    }

    #endregion

  }

}
